
package com.example.editlist;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RelativeLayout;

public class MainActivity extends Activity {

    private EditText mEdit;
    
    private ListView mList;
    
    private Handler mHandler;
    
    private TextPaint mPaint ;
//    private TextView mTextDesp;
    
    private RelativeLayout mListContainer;
//    private PopListWindow mPop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new Handler();
        initViews();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    private void initViews() {
        mListContainer = (RelativeLayout) findViewById(R.id.listContainer);
        mEdit = (EditText) findViewById(R.id.edittext);
        mPaint = mEdit.getPaint();
        mEdit.setOnTouchListener(new OnTouchListener() {
            
            @Override
            public boolean onTouch(View v, MotionEvent event) {
//                if( event.getAction() == MotionEvent.ACTION_UP){
//                    test();
//                }
                //暂时关闭调试
                if( event.getAction() == MotionEvent.ACTION_UP){
                    
                    //每次在edit区域点击up以后，50毫秒，检查光标位置
                    mHandler.postDelayed(new Runnable() {
                    	public void run() {
//                            Editable s = mEdit.getEditableText();
//                            String text = s.toString();
//                            
//                            int selectStart = mEdit.getSelectionStart();
//                            int selectEnd = mEdit.getSelectionEnd();
//
////                            getCursorLocateLine(text, selectStart);
//                            String curLine = getCursorLocateString(text, selectStart);
////                            Log.d("xxx", "index :"+selectStart);
//                            String curString = getCursorLineStringBeforeCursor(text, selectStart);
//                            setTitle(curString.replaceAll("\n", "_")+" ; "+curLine.replaceAll("\n", "_"));
//                            showList();
                    		//	TODO 测试使用EditText中的Layout获取光标的坐标
                    		showListNew();
                        }
                    }, 50);
                }else if(event.getAction() == MotionEvent.ACTION_DOWN){
                    mList.setVisibility(View.GONE);
                }
                return false;
            }
        });
        mEdit.addTextChangedListener(new TextWatcher() {
            //换行的处理逻辑，为了实现弹窗列表位置可控，需要精确控制EditText的换行操作，以方便计算当前光标的坐标
            /*1.监控输入的每个字符，如果最新输入的字符导致当前行的文本宽度超过了控件显示宽度，找到当前行末，增加换行符\r
             *2.弹框和当前输入光标对齐，并且弹框列表有一定宽度，为避免列表显示不完全，如果当前输入的字符（@加上一个字符）需要弹窗，
             *  则在@前加入换行符\r
             *3.所有的换行符不计入字符数，发布前这些换行符需要过滤
             *4.光标不一定在行尾，光标处插入的字符，可能导致本行超长一直换行
             * 
             */
            @Override
            public void afterTextChanged(Editable s){
//                AutoBreak(s);

            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }
        });
        ArrayList<String> list = new ArrayList<String>();
        list.add("测试数据1");
        list.add("测试数据2");
        list.add("测试数据3");
        list.add("测试数据4");
        list.add("测试数据5");
        list.add("测试数据6");
        list.add("测试数据7");
        list.add("测试数据8");
        list.add("测试数据9");
        list.add("测试数据10");
        list.add("测试数据11");
        list.add("测试数据12");
//        mPop = new PopListWindow(MainActivity.this, getLayoutInflater(),list, mEdit.getWidth() , null);
        mList = (ListView) findViewById(R.id.list);
        PopListAdapter adapter = new PopListAdapter(MainActivity.this, list,getLayoutInflater());
        mList.setAdapter(adapter);

        debug();
    }
    
    /**
     * 获得当前编辑框中所有的字符串字符串中有多少个\n,即多少行
     * 
     * @param input
     * @return
     */
    private static int getTotalLineCount(String input) {
        byte[] bytes = input.getBytes();
        // 默认至少一行
        int NCount = 1;
        for (byte b : bytes) {
            // 识别到一个换行符，行数+1
            if (b == '\n') {
                NCount += 1;
            }
        }
        return NCount;
    }
    
    String [] TEST = new String[]{"1","我","a他b","他ab","ab他","中b国","b中国","中国b"};
//    String [] TEST = new String[]{"我","冰菓","家拿到","亚美尼亚"};
    private void debug(){
       for(String tmp : TEST){
           for(int start = 0;start<=tmp.length();start++){
               for(int end = start;end<=tmp.length();end++){
                   if(start != end){
//                       Log.d("xxx", tmp +" sub from "+start +" to " +end+ " len "+(end - start)+" "+tmp.substring(start, end));
                   }
               }
           }
       }
    }
    
    private void test(){
        String tmp =mEdit.getText().toString();
        byte [] bytes = tmp.getBytes();
        String text="";
        try {
            text = new String(bytes , 0,bytes.length,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        int selectStart = mEdit.getSelectionStart();

        String curLine = getCursorLocateString(text, selectStart);
        int newStringWidth = (int) mPaint.measureText(curLine);
        int totalPadding = mEdit.getTotalPaddingLeft()-mEdit.getTotalPaddingRight();
        setTitle(newStringWidth+" / "+(mEdit.getWidth()- totalPadding));
//        setTitle("line :"+getCursorLocateLine(text, selectStart)+" - "+curLine.replaceAll("\n", "_"));
//        Log.d("xxx", "line :"+getCursorLocateLine(text, selectStart)+" - "+curLine.replaceAll("\n", "_"));
        Log.d("xxx", "["+curLine.replaceAll("\n", "_")+"]");
//        getCodeLenght(curLine);
        if(newStringWidth > mEdit.getWidth()){
            
        }else{
            
        }
    }
    
    /**
     * 处理输入文本时自动为光标行换行，注意用户在输入时一次能联想多个字，需要准确定位
     * @param s
     */
    private void AutoBreak(Editable s){
        String tmp =s.toString();
        byte [] bytes = tmp.getBytes();
        String text="";
        try {
            text = new String(bytes , 0,bytes.length,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        int selectStart = mEdit.getSelectionStart();

        String curLine = getCursorLocateString(text, selectStart).replace("\n", "");
        int newStringWidth = (int) mPaint.measureText(curLine);
        int totalPadding = mEdit.getTotalPaddingLeft()+mEdit.getTotalPaddingRight();
        setTitle(newStringWidth+" / "+(mEdit.getWidth()- totalPadding));
        Log.e("xxx", curLine.replaceAll("\n", "_"));
        Log.d("xxx", "["+subWidestStringWithinWidth(curLine, (mEdit.getWidth()- totalPadding), mPaint)+"]");
        //TODO 进一步完善人工添加的换行符的自动删除
        if((newStringWidth + totalPadding) > mEdit.getWidth()){
            String cursorStringAll = tmp.substring(0,selectStart);//原来输入的字符串，光标前的所有内容
            String sub = subWidestStringWithinWidth(curLine, (mEdit.getWidth()- totalPadding),mPaint);
            int lIndex = cursorStringAll.indexOf(sub);
            int rIndex = lIndex+sub.length();
            String rm = cursorStringAll.substring(rIndex);
            s.insert(rIndex, "\n");

            Log.d("xxx", "~~~"+lIndex+" , "+rIndex+" & "+rm);
        }else{
            
        }
    }
    /**
     * 获得光标所在行的行序号，从1开始
     * 
     * @param text        EditText中的全部文本内容
     * @param cursorIndex 光标的start
     * @return
     */
    private int getCursorLocateLine(String text, int cursorIndex) {

        int iChars = 0;
        int lineCount = 1;
        String subString = text.substring(0,cursorIndex);

//        Log.d("xxx", "getCursorLocateLine "+ cursorIndex +" : sub str "+subString);
        byte[] bytes = text.getBytes();
        char [] chars = text.toCharArray();
        int bCount = 0;
        int[] byteSize = getCodeLenght(text);
        for (char b : chars) {
             bCount += byteSize[iChars];
            iChars += 1;

            if (iChars <= cursorIndex - 1) {
                // 识别到一个换行符，行数+1
                if (bytes[bCount] == '\n') {
                    lineCount += 1;
                }

            }
        }
        return lineCount;
    }
    
    /**
     * 获得当前光标所在行的字符串
     * @param text
     * @param cursorIndex
     * @return
     */
    private String getCursorLocateString(String text, int cursorIndex) {

        String ret = "";
        int[] sizeArray = getDivIndexs(text);

        int curLine = getCursorLocateLine(text, cursorIndex);
//        Log.e("xxx", "cursor line "+curLine);
//        ret = text.substring(sizeArray[curLine - 1], sizeArray[curLine]);
//        Log.d("xxx", "getCursorLocateString"+sizeArray[curLine - 1]+" ; "+sizeArray[curLine ]);
//        ret = StringUtil.substring(text, sizeArray[curLine - 1], (sizeArray[curLine]-sizeArray[curLine - 1]));
        Log.d("xxx","getCursorLocateString : "+curLine +" ; "+cursorIndex);;
        ret = text.substring(sizeArray[curLine - 1], sizeArray[curLine]);
        //FIXME 增加删除字符时，光标位与字符串中间，导致计算出错
//        Log.d("xxx", "getCursorLocateString [" + ret.replaceAll("\n", "_") + "]");
        return ret;
    }
    
    /**
     * 获得当前行首到光标的内容
     * @param text
     * @param cursorIndex
     * @return
     */
    private String getCursorLineStringBeforeCursor(String text, int cursorIndex) {
      String ret ="";
      int[] sizeArray = getDivIndexs(text);
      int curLine = getCursorLocateLine(text, cursorIndex);
//      ret = text.substring(sizeArray[curLine-1],cursorIndex);
//      Log.d("xxx", ""+sizeArray[curLine-1]+","+cursorIndex);
//      ret = StringUtil.substring(text, sizeArray[curLine-1], cursorIndex-sizeArray[curLine-1]);
      ret = text.substring(sizeArray[curLine-1],cursorIndex);
//      Log.d("xxx", "getCursorLineStringBeforeCursor["+ret+"]");
      return ret;
  }

    private int[] getDivIndexs(String text) {
        int [] sizeArray = new int[getTotalLineCount(text)+1];
        int [] sizeRetArray = new int[getTotalLineCount(text)+1];
        String debug = "";
        int iChars = 0;
        int lineCount = 1;
        sizeArray[0]=0;
        byte[] bytes = text.getBytes();

        debug += "["+sizeArray[0]+"]";
        for (byte b : bytes) {
            
                // 识别到一个换行符，行数+1
                if (b == '\n') {
                    sizeArray[lineCount] = iChars;
                    debug += "["+sizeArray[lineCount]+"]";
                    lineCount += 1;
                    
                }
                iChars += 1;
        }
        sizeArray[sizeArray.length-1] = bytes.length;
        debug += "["+sizeArray[sizeArray.length-1]+"]";
//        Log.e("xxx", debug);
        
        sizeRetArray[0]=0;
        debug="[0]";
        for(int i = 1;i<sizeArray.length;i++){
            String tmp="";
            try {
                tmp = new String(bytes , sizeArray[i-1] , (sizeArray[i]-sizeArray[i-1]),"UTF-8");
                sizeRetArray[i] = tmp.length()+sizeRetArray[i-1];
                debug+="["+sizeRetArray[i]+"]";
            } catch (Exception e) {
//                Log.e("xxx", e.getMessage());
            }
//            Log.d("xxx", "getDivIndexs["+i+"]-"+tmp);
        }
        Log.e("xxx","getDivIndexs -"+ debug);
        return sizeRetArray;
    }

    /**
     * 获得光标在当前行的序号
     * @param text
     * @param cursorIndex
     * @return
     */
    private int getCursorDivIndex(String text, int cursorIndex) {
        int[] sizeArray = getDivIndexs(text);

        int curLine = getCursorLocateLine(text, cursorIndex);
        int ret = cursorIndex - sizeArray[curLine - 1];

        return ret;
    }
    /*
     * 显示list的入口，list和光标保持对齐
     */
    private void showList() {
        
        Editable s = mEdit.getEditableText();
        String text = s.toString();
        int topPadding = mEdit.getTotalPaddingTop();
        int leftPadding = mEdit.getPaddingLeft();
        int selectStart = mEdit.getSelectionStart();
        this.getCursorLocateLine(text, selectStart);
        int lineHeight = mEdit.getLineHeight();
        mList.setVisibility(View.VISIBLE);
        mListContainer.setVisibility(View.VISIBLE);
        int i = getCursorDivIndex(text, selectStart);
        int h = getCursorLocateLine(text, selectStart);
//        int [] div = getDivIndexs(text);
        
        TextPaint tp = mEdit.getPaint();
        int w = (int)tp.measureText(getCursorLineStringBeforeCursor(text, selectStart));
        //Log.d("xxx","cursor pop-"+i+" h-"+h);
        
        mListContainer.setPadding(w+leftPadding, lineHeight * h + topPadding, 0, 0);
    }
    
    private void showListNew() {
        
    	Layout layout = mEdit.getLayout();
    	int pos = mEdit.getSelectionStart();
    	int line = layout.getLineForOffset(pos);
    	int baseline = layout.getLineBaseline(line);
    	int ascent = layout.getLineAscent(line);
    	int x = (int) layout.getPrimaryHorizontal(pos);
    	int y = baseline ;
    	Log.d("xxx", "line :"+line+" ; x "+x+" y "+y);
        mList.setVisibility(View.VISIBLE);
        mListContainer.setVisibility(View.VISIBLE);
        mListContainer.setPadding(x, y, 0, 0);
    }
    /**
     * 逐个字符计算(一行)文字宽度
     * @param input 传入的文字为一行文字
     * @return
     */
    private int getStringWidth(String input) {
        float w = 0f;
        int iCount = 0;
        if (TextUtils.isEmpty(input)) {
            return 0;
        } else {
            while (iCount < input.length() - 1) {
                String b = input.substring(iCount, iCount + 1);
                w += mPaint.measureText(b);
                iCount+=1;
            }
            return (int)w;
        }
        
    }
    
    private int [] getCodeLenght(String input){
        if(TextUtils.isEmpty(input)){
            return null;
        }else{
            int len = input.length();
            byte[] bytes = input.getBytes();
            int []code = new int[len];
            for(int i=1;i<=len;i++){
                String sub = input.substring(i-1,i);
                int codeLen = sub.getBytes().length;
                code[i-1] = codeLen;            
            }
            String debug="";
            for(int i :code){
                debug+="[" +i+"]";
            }
            Log.d("xxx", debug);
            return code;
        }
    }
    /**
     * 指定一行字符串，从头开始，找到不超出maxWidth宽度的最长字符创
     * @param input
     * @param maxWidth
     * @return
     */
    private String subWidestStringWithinWidth(String input , int maxWidth,TextPaint tp){
        if(TextUtils.isEmpty(input)||tp == null|| maxWidth <=0){
            return "";
        }else{
            int end = input.length();
            int width = 0;
            String tmp = "";
            while(end > 0){
                tmp = input.substring(0, end);
                width = (int) tp.measureText(tmp);
                if(width < maxWidth){
                    break;
                }else{
                    end--;
                }
            }
            return tmp;
        }
    }
    
    private void testTextLayout(){
    	Layout layout = mEdit.getLayout();
    	int pos = mEdit.getSelectionStart();
    	int line = layout.getLineForOffset(pos);
    	int baseline = layout.getLineBaseline(line);
    	int ascent = layout.getLineAscent(line);
    	float x = layout.getPrimaryHorizontal(pos);
    	float y = baseline + ascent;
    	Log.d("xxx", "line :"+line+" ; baseline "+baseline+" ascend "+ascent);
    	showList();
    }
}
